﻿using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB;
using YJKRevitBase.Math;
using YJKRevitDimensionText.VerticalAutoDim.Comparer;
using YJKRevitDimensionText.VerticalAutoDim.GeoModel;

namespace YJKRevitDimensionText.VerticalAutoDim.Group
{
	internal class GeoGroupManager
	{
		private IList<List<IPlanarGeo>> ArrayWindDoorGeoSortedByLevel { get; set; }

		public IList<GeoGroupNode> ArrayGeoGroupNode { get; set; }

		public IList<GeoVerticalDimResult> ArrayGeoVerticalDimResult { get; set; }

		public List<IPlanarGeo> ArrayWindDoorGeo
		{
			get
			{
				return this.GeoGroupData.ArrayWindDoorGeo.ToList<IPlanarGeo>();
			}
		}

		public GeoGroupManager(GeoGroupData geoGroupData, XYZ ptBoundaryMinWcs, XYZ ptBoundaryMaxWcs, Transform transformWcs2Ecs, Autodesk.Revit.DB.Document doc)
		{
			this.Doc = doc;
			this.PointBoundaryMinWcs = ptBoundaryMinWcs;
			this.PointBoundaryMaxWcs = ptBoundaryMaxWcs;
			this.TransformWcs2Ecs = transformWcs2Ecs;
			this.GeoGroupData = geoGroupData;
			this.ArrayGeoGroupNode = new List<GeoGroupNode>();
			this.ArrayGeoVerticalDimResult = new List<GeoVerticalDimResult>();
		}

		private bool Init()
		{
			return true;
		}

		private bool CalcVirticalGeoGroupNodeDim()
		{
			bool result;
			try
			{
				XYZ ptLeftDimPos = null;
				XYZ ptRightDimPos = null;
				this.GetFirstAndSecondDimRef(ref ptLeftDimPos, ref ptRightDimPos);
				List<IPlanarGeo> thirdDimRef = this.GetThirdDimRef(ptLeftDimPos, ptRightDimPos);
				if (this.GeoGroupData.ArrayWindDoorGeo.Count != 0)
				{
					this.GetWinDoorDimRef(thirdDimRef);
				}
				result = true;
			}
			catch (Exception ex)
			{
				throw ex;
			}
			return result;
		}

		private bool CalcVirticalWindDoorGeoDim()
		{
			if (this.GeoGroupData.ArrayWindDoorGeo.Count == 0)
			{
				return true;
			}
			try
			{
				double y = this.TransformWcs2Ecs.OfPoint(this.PointBoundaryMinWcs).Y;
				double y2 = this.TransformWcs2Ecs.OfPoint(this.PointBoundaryMaxWcs).Y;
				IList<GeoVerticalDimResult> source = new GeoVerticalSearch(this.ArrayWindDoorGeoSortedByLevel, y, y2).Calc();
				this.ArrayGeoVerticalDimResult = source.Distinct<GeoVerticalDimResult>().ToList<GeoVerticalDimResult>();
				this.InsertLevelIntoGroup();
				return true;
			}
			catch
			{
			}
			return false;
		}

		public bool Calc()
		{
			return this.Init() && this.CalcVirticalGeoGroupNodeDim() && this.CalcVirticalWindDoorGeoDim();
		}

		private void GetFirstAndSecondDimRef(ref XYZ ptLeftDimPos, ref XYZ ptRightDimPos)
		{
			IList<IPlanarGeo> arrayLevelGeo = this.GeoGroupData.ArrayLevelGeo;
			if (arrayLevelGeo.Count == 0)
			{
				return;
			}
			try
			{
				double num = UnitUtility.MMToFeet(3200.0);
				double num2 = UnitUtility.MMToFeet(800.0);
				XYZ xyz = this.TransformWcs2Ecs.OfPoint(this.PointBoundaryMinWcs);
				if (MathUtility.LessThan(this.TransformWcs2Ecs.OfPoint(this.PointBoundaryMaxWcs).X, xyz.X))
				{
					ptLeftDimPos = this.PointBoundaryMaxWcs;
					ptRightDimPos = this.PointBoundaryMinWcs;
				}
				else
				{
					ptLeftDimPos = this.PointBoundaryMinWcs;
					ptRightDimPos = this.PointBoundaryMaxWcs;
				}
				XYZ xyz2 = this.Doc.ActiveView.RightDirection.Normalize();
				ptLeftDimPos -= xyz2 * num;
				ptRightDimPos += xyz2 * num;
				if (2 <= arrayLevelGeo.Count)
				{
					List<IPlanarGeo> list = new List<IPlanarGeo>();
					list.Add(arrayLevelGeo[0]);
					list.Add(arrayLevelGeo[arrayLevelGeo.Count - 1]);
					this.AddArrayGeoGroupNode(list, ptLeftDimPos, true);
					this.AddArrayGeoGroupNode(list, ptRightDimPos, false);
				}
				if (2 < arrayLevelGeo.Count)
				{
					ptLeftDimPos += xyz2 * num2;
					ptRightDimPos -= xyz2 * num2;
					this.AddArrayGeoGroupNode(arrayLevelGeo, ptLeftDimPos, true);
					this.AddArrayGeoGroupNode(arrayLevelGeo, ptRightDimPos, false);
				}
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		private List<IPlanarGeo> GetThirdDimRef(XYZ ptLeftDimPos, XYZ ptRightDimPos)
		{
			List<IPlanarGeo> result;
			try
			{
				double num = UnitUtility.MMToFeet(800.0);
				XYZ xyz = this.Doc.ActiveView.RightDirection.Normalize();
				ptLeftDimPos += xyz * num;
				ptRightDimPos -= xyz * num;
				List<IPlanarGeo> list = new List<IPlanarGeo>();
				IList<IPlanarGeo> thirdDimRef = this.GetThirdDimRef(true);
				this.AddArrayGeoGroupNode(thirdDimRef, ptLeftDimPos, true);
				this.RemoveSameElement(ref list, thirdDimRef);
				thirdDimRef = this.GetThirdDimRef(false);
				this.AddArrayGeoGroupNode(thirdDimRef, ptRightDimPos, false);
				this.RemoveSameElement(ref list, thirdDimRef);
				result = list;
			}
			catch (Exception ex)
			{
				throw ex;
			}
			return result;
		}

		private void GetWinDoorDimRef(List<IPlanarGeo> lstThirdDimRefGeo)
		{
			if (lstThirdDimRefGeo.Count == 0)
			{
				return;
			}
			try
			{
				Dictionary<IPlanarGeo, List<IPlanarGeo>> dictionary = this.WinDoorGroupByLevel();
				this.ArrayWindDoorGeoSortedByLevel = new List<List<IPlanarGeo>>();
				foreach (KeyValuePair<IPlanarGeo, List<IPlanarGeo>> keyValuePair in dictionary)
				{
					if (keyValuePair.Value.Count == 0)
					{
						this.ArrayWindDoorGeoSortedByLevel.Add(new List<IPlanarGeo>());
					}
					else
					{
						new List<IPlanarGeo>();
						foreach (IPlanarGeo planarGeo in lstThirdDimRefGeo)
						{
							for (int i = 0; i < keyValuePair.Value.Count; i++)
							{
								IPlanarGeo planarGeo2 = keyValuePair.Value[i];
								if (planarGeo2.Element.Id == planarGeo.Element.Id)
								{
									keyValuePair.Value.RemoveAt(i);
									i--;
								}
								else if (MathUtility.IsEqual(planarGeo.BottomElev - planarGeo2.BottomElev, 0.0) && MathUtility.IsEqual(planarGeo.TopElev - planarGeo2.TopElev, 0.0))
								{
									keyValuePair.Value.RemoveAt(i);
									i--;
								}
							}
						}
						this.ArrayWindDoorGeoSortedByLevel.Add(keyValuePair.Value);
					}
				}
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		private IList<IPlanarGeo> GetThirdDimRef(bool bByLeft)
		{
			Dictionary<IPlanarGeo, List<IPlanarGeo>> dictionary = this.WinDoorGroupByLevelAndDirection(bByLeft);
			if (dictionary == null)
			{
				return null;
			}
			Dictionary<IPlanarGeo, List<IPlanarGeo>> dictionary2 = this.ThirdDimRefByLevelAndPos(dictionary, bByLeft);
			if (dictionary2 == null)
			{
				return null;
			}
			List<IPlanarGeo> list = new List<IPlanarGeo>();
			foreach (KeyValuePair<IPlanarGeo, List<IPlanarGeo>> keyValuePair in dictionary2)
			{
				list.Add(keyValuePair.Key);
				if (0 < keyValuePair.Value.Count)
				{
					list.AddRange(keyValuePair.Value);
				}
			}
			this.GetTopGeoByRegion(ref list, bByLeft);
			return list;
		}

		private void AddArrayGeoGroupNode(IList<IPlanarGeo> arrayGeo, XYZ point, bool bByLeft = true)
		{
			GeoGroupNode geoGroupNode = new GeoGroupNode();
			geoGroupNode.ArrayGeo = arrayGeo;
			geoGroupNode.Point = point;
			geoGroupNode.Position = bByLeft;
			this.ArrayGeoGroupNode.Add(geoGroupNode);
		}

		private Dictionary<IPlanarGeo, List<IPlanarGeo>> WinDoorGroupByLevel()
		{
			if (this.GeoGroupData.ArrayWindDoorGeo.Count == 0)
			{
				return null;
			}
			Dictionary<IPlanarGeo, List<IPlanarGeo>> result;
			try
			{
				Dictionary<IPlanarGeo, List<IPlanarGeo>> dictionary = new Dictionary<IPlanarGeo, List<IPlanarGeo>>();
				List<IPlanarGeo> lstWinDoorGeo = new List<IPlanarGeo>();
				EnumerableExternsion.ForEach<IPlanarGeo>(this.GeoGroupData.ArrayWindDoorGeo, delegate(IPlanarGeo v)
				{
					lstWinDoorGeo.Add(v);
				});
				for (int i = 0; i < this.GeoGroupData.ArrayLevelGeo.Count; i++)
				{
					double bottomElev = this.GeoGroupData.ArrayLevelGeo[i].BottomElev;
					double num;
					if (i == this.GeoGroupData.ArrayLevelGeo.Count - 1)
					{
						num = double.MaxValue;
					}
					else
					{
						num = this.GeoGroupData.ArrayLevelGeo[i + 1].BottomElev;
					}
					List<IPlanarGeo> list = new List<IPlanarGeo>();
					for (int j = 0; j < lstWinDoorGeo.Count; j++)
					{
						IPlanarGeo planarGeo = lstWinDoorGeo[j];
						if (MathUtility.Greaterthan_Or_Equal(planarGeo.BottomElev, bottomElev, 1E-09) && MathUtility.LessThan(planarGeo.BottomElev, num, 1E-09))
						{
							list.Add(planarGeo);
							lstWinDoorGeo.RemoveAt(j);
							j--;
						}
					}
					list.Sort(new GeoComparerAtEqualLevel(true));
					dictionary.Add(this.GeoGroupData.ArrayLevelGeo[i], list);
				}
				result = dictionary;
			}
			catch (Exception ex)
			{
				throw ex;
			}
			return result;
		}

		private Dictionary<IPlanarGeo, List<IPlanarGeo>> WinDoorGroupByLevelAndDirection(bool bByLeft = true)
		{
			if (this.GeoGroupData.ArrayWindDoorGeo.Count == 0)
			{
				return null;
			}
			Dictionary<IPlanarGeo, List<IPlanarGeo>> result;
			try
			{
				Dictionary<IPlanarGeo, List<IPlanarGeo>> dictionary = new Dictionary<IPlanarGeo, List<IPlanarGeo>>();
				List<IPlanarGeo> winDoorGeoByDirection = this.GetWinDoorGeoByDirection(bByLeft);
				for (int i = 0; i < this.GeoGroupData.ArrayLevelGeo.Count; i++)
				{
					double bottomElev = this.GeoGroupData.ArrayLevelGeo[i].BottomElev;
					double num;
					if (i == this.GeoGroupData.ArrayLevelGeo.Count - 1)
					{
						num = double.MaxValue;
					}
					else
					{
						num = this.GeoGroupData.ArrayLevelGeo[i + 1].BottomElev;
					}
					List<IPlanarGeo> list = new List<IPlanarGeo>();
					for (int j = 0; j < winDoorGeoByDirection.Count; j++)
					{
						IPlanarGeo planarGeo = winDoorGeoByDirection[j];
						if (MathUtility.Greaterthan_Or_Equal(planarGeo.BottomElev, bottomElev, 1E-09) && MathUtility.LessThan(planarGeo.BottomElev, num, 1E-09))
						{
							list.Add(planarGeo);
							winDoorGeoByDirection.RemoveAt(j);
							j--;
						}
					}
					list.Sort(new GeoComparerAtEqualLevel(bByLeft));
					dictionary.Add(this.GeoGroupData.ArrayLevelGeo[i], list);
				}
				result = dictionary;
			}
			catch (Exception ex)
			{
				throw ex;
			}
			return result;
		}

		private List<IPlanarGeo> GetWinDoorGeoByDirection(bool bByLeft = true)
		{
			List<IPlanarGeo> result;
			try
			{
				XYZ xyz = (this.PointBoundaryMinWcs + this.PointBoundaryMaxWcs) / 2.0;
				xyz = this.TransformWcs2Ecs.OfPoint(xyz);
				List<IPlanarGeo> list = new List<IPlanarGeo>();
				foreach (IPlanarGeo planarGeo in this.GeoGroupData.ArrayWindDoorGeo)
				{
					if (bByLeft)
					{
						if (MathUtility.LessThan(planarGeo.Min2DPointECS.X, xyz.X, 1E-06))
						{
							list.Add(planarGeo);
						}
					}
					else if (MathUtility.GreaterThan(planarGeo.Max2DPointECS.X, xyz.X, 1E-06))
					{
						list.Add(planarGeo);
					}
				}
				result = list;
			}
			catch (Exception ex)
			{
				throw ex;
			}
			return result;
		}

		private Dictionary<IPlanarGeo, List<IPlanarGeo>> ThirdDimRefByLevelAndPos(Dictionary<IPlanarGeo, List<IPlanarGeo>> dictWinDoorGroupByLevel, bool bByLeft = true)
		{
			if (dictWinDoorGroupByLevel == null)
			{
				return null;
			}
			Dictionary<IPlanarGeo, List<IPlanarGeo>> result;
			try
			{
				Dictionary<IPlanarGeo, List<IPlanarGeo>> dictionary = new Dictionary<IPlanarGeo, List<IPlanarGeo>>();
				foreach (KeyValuePair<IPlanarGeo, List<IPlanarGeo>> keyValuePair in dictWinDoorGroupByLevel)
				{
					if (1 >= keyValuePair.Value.Count)
					{
						dictionary.Add(keyValuePair.Key, keyValuePair.Value);
					}
					else
					{
						IPlanarGeo planarGeo = keyValuePair.Value[0];
						List<IPlanarGeo> list = new List<IPlanarGeo>();
						list.Add(planarGeo);
						for (int i = 1; i < keyValuePair.Value.Count; i++)
						{
							IPlanarGeo planarGeo2 = keyValuePair.Value[i];
							if (bByLeft)
							{
								if (MathUtility.IsEqual(planarGeo.Min2DPointECS.X - planarGeo2.Min2DPointECS.X, 0.0))
								{
									list.Add(planarGeo2);
								}
							}
							else if (MathUtility.IsEqual(planarGeo.Max2DPointECS.X - planarGeo2.Max2DPointECS.X, 0.0))
							{
								list.Add(planarGeo2);
							}
						}
						dictionary.Add(keyValuePair.Key, list);
					}
				}
				result = dictionary;
			}
			catch
			{
				result = null;
			}
			return result;
		}

		private void RemoveSameElement(ref List<IPlanarGeo> lstAllThirdRef, IList<IPlanarGeo> lstThirdDimRef)
		{
			if (lstThirdDimRef.Count == 0)
			{
				return;
			}
			try
			{
				foreach (IPlanarGeo planarGeo in lstThirdDimRef)
				{
					bool flag = false;
					foreach (IPlanarGeo planarGeo2 in lstAllThirdRef)
					{
						if (planarGeo.Element.Id == planarGeo2.Element.Id)
						{
							flag = true;
							break;
						}
					}
					if (!flag)
					{
						lstAllThirdRef.Add(planarGeo);
					}
				}
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		private void InsertLevelIntoGroup()
		{
			for (int i = 0; i < this.ArrayGeoVerticalDimResult.Count; i++)
			{
				GeoVerticalDimResult geoVerticalDimResult = this.ArrayGeoVerticalDimResult[i];
				int count = geoVerticalDimResult.ArrayGeo.Count;
				for (int j = 0; j < count; j++)
				{
					IPlanarGeo planarGeo = geoVerticalDimResult.ArrayGeo[j];
					for (int k = this.GeoGroupData.ArrayLevelGeo.Count - 1; k >= 0; k--)
					{
						IPlanarGeo planarGeo2 = this.GeoGroupData.ArrayLevelGeo[k];
						if (MathUtility.Greaterthan_Or_Equal(planarGeo.BottomElev, planarGeo2.BottomElev, 1E-06))
						{
							geoVerticalDimResult.ArrayGeo.Add(planarGeo2);
							break;
						}
					}
				}
			}
		}

		private void GetTopGeoByRegion(ref List<IPlanarGeo> lstThirdDimRef, bool bByLeft = true)
		{
			XYZ xyz = this.TransformWcs2Ecs.OfPoint((this.PointBoundaryMinWcs + this.PointBoundaryMaxWcs) / 2.0);
			IPlanarGeo parapetOrRailingGeo = this.GetParapetOrRailingGeo(xyz, bByLeft);
			if (bByLeft)
			{
				if (parapetOrRailingGeo != null)
				{
					if (MathUtility.LessThan(parapetOrRailingGeo.Min2DPointECS.X, xyz.X, 1E-06))
					{
						lstThirdDimRef.Add(parapetOrRailingGeo);
						return;
					}
					return;
				}
				else
				{
					using (List<IPlanarGeo>.Enumerator enumerator = this.GeoGroupData.ArrayTopMostGeo.GetEnumerator())
					{
						while (enumerator.MoveNext())
						{
							IPlanarGeo planarGeo = enumerator.Current;
							if (MathUtility.LessThan(planarGeo.Min2DPointECS.X, xyz.X, 1E-06))
							{
								lstThirdDimRef.Add(planarGeo);
								break;
							}
						}
						return;
					}
				}
			}
			if (parapetOrRailingGeo != null)
			{
				if (MathUtility.GreaterThan(parapetOrRailingGeo.Max2DPointECS.X, xyz.X, 1E-06))
				{
					lstThirdDimRef.Add(parapetOrRailingGeo);
					return;
				}
			}
			else
			{
				foreach (IPlanarGeo planarGeo2 in this.GeoGroupData.ArrayTopMostGeo)
				{
					if (MathUtility.GreaterThan(planarGeo2.Max2DPointECS.X, xyz.X, 1E-06))
					{
						lstThirdDimRef.Add(planarGeo2);
						break;
					}
				}
			}
		}

		private IPlanarGeo GetParapetOrRailingGeo(XYZ ptMiddleEcs, bool bByLeft = true)
		{
			IPlanarGeo planarGeo = null;
			foreach (IPlanarGeo planarGeo2 in this.GeoGroupData.ArrayTopMostGeo)
			{
				if (planarGeo2.Weight == 8)
				{
					if (bByLeft)
					{
						if (!MathUtility.LessThan(planarGeo2.Min2DPointECS.X, ptMiddleEcs.X, 1E-06))
						{
							continue;
						}
					}
					else if (!MathUtility.GreaterThan(planarGeo2.Max2DPointECS.X, ptMiddleEcs.X, 1E-06))
					{
						continue;
					}
					if (planarGeo == null)
					{
						planarGeo = planarGeo2;
					}
					else if (MathUtility.LessThan(planarGeo.TopElev, planarGeo2.TopElev, 1E-06))
					{
						planarGeo = planarGeo2;
					}
				}
			}
			return planarGeo;
		}

		private readonly Document Doc;

		private readonly XYZ PointBoundaryMinWcs;

		private readonly XYZ PointBoundaryMaxWcs;

		private readonly Transform TransformWcs2Ecs;

		private readonly GeoGroupData GeoGroupData;
	}
}
